package Reaction::InterfaceModel::Action::DBIC::Role::CheckUniques;

use Reaction::Role;

#has '_unique_constraint_results' => (
#  isa => 'HashRef',
#  is => 'rw',
#  required => 1,
#  default => sub { {} },
#  metaclass => 'Moose::Meta::Attribute'
#);

role CheckUniques which {

  implements check_all_uniques => as {
    my ($self) = @_;
    my $source = $self->target_model->result_source;
    my %uniques = $source->unique_constraints;
    my $proto = ($self->target_model->isa('DBIx::Class::ResultSet')
                   ? $self->target_model->new_result({})
                   : $self->target_model);
    my $param_hr = $self->parameter_hashref;
    my %proto_hash = (
      map {
        my @ret;
        my $attr = $proto->meta->find_attribute_by_name($_->name);
        if ($attr) {
          my $reader = $attr->reader || $attr->accessor;
          if ($reader) {
            my $value = $proto->$reader;
            if (defined($value)) {
              @ret = ($_->name => $value);
            }
          }
        }
        @ret;
      } $self->parameter_attributes
    );
    my %merged = (
      %proto_hash,
      (map {
        (defined $param_hr->{$_} ? ($_ => $param_hr->{$_}) : ());
      } keys %$param_hr),
    );
    my %ident = %{$proto->ident_condition};
    my %clashes;
    my $rs = $source->resultset;
    foreach my $unique (keys %uniques) {
      my %pass;
      my @attrs = @{$uniques{$unique}};
      next if grep { !exists $merged{$_} } @attrs;
        # skip PK before insertion if auto-inc etc. etc.
      @pass{@attrs} = @merged{@attrs};
      if (my $obj = $rs->find(\%pass, { key => $unique })) {
        my $found_ident = $obj->ident_condition;
  #warn join(', ', %$found_ident, %ident);
        if (!$proto->in_storage
            || (grep { $found_ident->{$_} ne $ident{$_} } keys %ident)) {
          # if in storage and no ident conditions are different the found
          # obj is *us* :)
          $clashes{$_} = 1 for @attrs;
        }
      }
    }
    $self->_unique_constraint_results(\%clashes);
  };
  
  after sync_all => sub { shift->check_all_uniques; };
  
  override error_for_attribute => sub {
    my ($self, $attr) = @_;
    if ($self->_unique_constraint_results->{$attr->name}) {
      return "Already taken, please try an alternative";
    }
    return super();
  };
  
  override can_apply => sub {
    my ($self) = @_;
    return 0 if keys %{$self->_unique_constraint_results};
    return super();
  };

};

1;
